package com.ynet.middleground.approve.service.impl;

import java.util.List;

import javax.validation.Valid;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;

import org.activiti.engine.task.Task;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;

import com.google.common.base.Throwables;
import com.ynet.core.codec.mark.util.ObjectUtil;
import com.ynet.core.common.ServiceResult;
import com.ynet.core.exception.BusinessException;
import com.ynet.core.log.IfpLogger;
import com.ynet.middleground.approve.bean.*;
import com.ynet.middleground.approve.dto.HistoryTaskDTO;
import com.ynet.middleground.approve.dto.ProcessInstanceDTO;
import com.ynet.middleground.approve.model.ProcessHistoryModel;
import com.ynet.middleground.approve.model.ProcessRuntimeModel;
import com.ynet.middleground.approve.model.ProcessTaskModel;
import com.ynet.middleground.approve.service.ProcessInstanceService;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;

/**
 * @author liulx
 * @description 流程实例相关接口服务实现类
 * @date 2019/10/11 13:59
 */
@Api(value = "流程实例相关接口服务", tags = {"流程实例相关接口服务"})
@Produces({MediaType.APPLICATION_JSON + "; " + "charset=UTF-8"})
@Consumes({MediaType.APPLICATION_JSON + "; " + "charset=UTF-8"})
@Path("instances")
@Service(timeout = 6000, version = "0.0.1")
public class ProcessInstanceServiceImpl implements ProcessInstanceService {

    @Autowired
    private ProcessRuntimeModel processRuntimeModel;

    @Autowired
    private ProcessHistoryModel processHistoryModel;

    @Autowired
    private ProcessTaskModel processTaskModel;

    /**
     * 挂起或激活指定流程的实例
     *
     * @param req 激活流程对象: {流程实例id, 激活挂起标志}
     * @return 挂起或激活标志,以success 字段值标识 true/false
     */
    @PUT
    @ApiOperation(value = "挂起或激活指定流程实例", notes = "挂起或激活指定流程实例，对于已经发起的审批流程，可以进行挂起操作，挂起后审批流程无法继续操作，激活后可以继续操作审批流程")
    @ApiImplicitParam(name = "requestObj", dataType = "ActivateProcessInstanceReq", paramType = "body", required = true)
    @Override
    public ServiceResult suspendOrActiveProcessInstance(ActivateProcessInstanceReq req) {
        ServiceResult<Object> result = new ServiceResult<>();
        try {
            processRuntimeModel.suspendOrActiveProcessInstance(req);
        } catch (BusinessException be) {
            result.setError(be.getCode(), be.getMessage());
        } catch (Exception e) {
            IfpLogger.error(Thread.currentThread().getStackTrace()[1].getMethodName(), "activiti挂起或激活指定流程实例异常，case: {}",
                Throwables.getStackTraceAsString(e));
            result.setError("ECAP0000", "服务异常，请联系系统管理员");
            return result;
        }
        return result;
    }

    /**
     * 根据流程实例ID查询流程是否结束 <br>
     *
     * @param req 流程是否结束请求对象: {流程实例id}
     * @return 流程对象，如果是空，则结束; 非空, 未结束
     */
    @POST
    @Path("finish")
    @ApiOperation(value = "根据流程实例ID查询流程是否结束", notes = "查询当前审批流程实例，判断审批流程是否已经审批结束，判断依据是当前运行记录是否有数据，没有则认为已结束")
    @ApiImplicitParam(name = "requestObj", dataType = "ProcessInstanceReq", paramType = "body", required = true)
    @Override
    public ServiceResult<ProcessInstanceDTO> isFinishProcess(ProcessInstanceReq req) {
        ServiceResult<ProcessInstanceDTO> result = new ServiceResult<>();
        try {
            result.setResult(processRuntimeModel.isFinishProcess(req));
        } catch (BusinessException be) {
            result.setError(be.getCode(), be.getMessage());
        } catch (Exception e) {
            IfpLogger.error(Thread.currentThread().getStackTrace()[1].getMethodName(),
                "activiti根据流程实例ID查询运行时流程异常，case: {}", Throwables.getStackTraceAsString(e));
            result.setError("ECAP0000", "服务异常，请联系系统管理员");
            return result;
        }
        return result;
    }

    /**
     * 根据发起人查询用户发起的未结束的流程列表 <br />
     * <note> 考虑是否需要添加分页-目前考虑不需要，查询用户发起的流程，应由渠道查询自己具体的业务表</note>
     * 
     * @param req 查询用户发起的流程列表: {流程发起人}
     * @return 由用户发起的未结束的流程列表
     */
    @POST
    @Path("unfinished")
    @ApiOperation(value = "根据发起人查询用户发起的未结束的流程列表", notes = "根据流程发起人，查询用户发起的未结束的流程，并返回具体的流程列表信息")
    @ApiImplicitParam(name = "requestObj", dataType = "QueryRunningProcessReq", paramType = "body", required = true)
    @Override
    public ServiceResult<List<ProcessInstanceDTO>> listUnfinishedProcessByStartUserId(QueryRunningProcessReq req) {
        ServiceResult<List<ProcessInstanceDTO>> result = new ServiceResult<>();
        try {
            List<ProcessInstanceDTO> list = processHistoryModel.listUnfinishedProcessByStartUserId(req);
            // List<ProcessInstanceDTO> list = processHistoryModel.listProcessInstanceByStartUserId(processInfoReq);
            result.setResult(list);
        } catch (BusinessException be) {
            result.setError(be.getCode(), be.getMessage());
        } catch (Exception e) {
            IfpLogger.error(Thread.currentThread().getStackTrace()[1].getMethodName(),
                "activiti查询用户发起的未结束流程列表信息异常，case: {}", Throwables.getStackTraceAsString(e));
            result.setError("ECAP0000", "服务异常，请联系系统管理员");
            return result;
        }
        result.setPagerInfo(req.getPagerInfo());
        return result;
    }

    /**
     * 判断用户是否参与了未结束的流程(包括用户发起的流程、需要用户正在审核的流程)
     *
     * @param req 流程信息请求对象: {流程发起人}
     * @return 结果标志: true/false
     */
    @POST
    @Path("check-join-process")
    @ApiOperation(value = "判断用户是否参与了未结束的流程", notes = "判断用户是否参与了未结束的流程(包括用户发起的流程、需要用户正在审核的流程)，只查询的是未结束的审批流程")
    @ApiImplicitParam(name = "requestObj", dataType = "QueryRunningProcessReq", paramType = "body", required = true)
    @Override
    public ServiceResult<Boolean> isJoinUnfinishedProcess(QueryRunningProcessReq req) {
        ServiceResult<Boolean> result = new ServiceResult<>();
        try {
            Boolean joinUnfinishedProcess = processRuntimeModel.isJoinUnfinishedProcess(req);
            result.setResult(joinUnfinishedProcess);
        } catch (BusinessException be) {
            result.setError(be.getCode(), be.getMessage());
        } catch (Exception e) {
            IfpLogger.error(Thread.currentThread().getStackTrace()[1].getMethodName(),
                "activiti用户是否参与了未结束的流程业务异常，case: {}", Throwables.getStackTraceAsString(e));
            result.setError("ECAP0000", "服务异常，请联系系统管理员");
            return result;
        }
        return result;
    }

    /**
     * 启动流程-通过流程定义Key(模板ID) 、业务Key启动流程
     *
     * @param startProcessInstanceReq 启动流程请求对象: {流程定义Key, 发起人, 关联业务, 参数}
     * @return 流程实例ID
     */
    @POST
    @ApiOperation(value = "发起审批流程",
        notes = "通过流程定义Key(模板ID) 、业务Key发起一个审批流程，返回流程实例id，对接此接口需要与中台系统技术开发进行沟通请求参数如何传递，此接口比较特殊")
    @ApiImplicitParam(name = "requestObj", dataType = "StartProcessInstanceReq", paramType = "body", required = true)
    @Override
    public ServiceResult<String> startProcessInstance(StartProcessInstanceReq startProcessInstanceReq) {
        ServiceResult<String> result = new ServiceResult<>();
        try {
            Task task = processRuntimeModel.startProcessInstance(startProcessInstanceReq);
            result.setResult(task.getProcessInstanceId());
            // 发送 dingtalk 通知
            processTaskModel.sendDingtalkNotification(task.getId(),
                ObjectUtil.objectToString(startProcessInstanceReq.getOperationUserId()));
        } catch (BusinessException be) {
            result.setError(be.getCode(), be.getMessage());
        } catch (Exception e) {
            IfpLogger.error(Thread.currentThread().getStackTrace()[1].getMethodName(), "发起流程业务异常，case: {}",
                Throwables.getStackTraceAsString(e));
            result.setError("ECAP0000", "服务异常，请联系系统管理员");
            return result;
        }
        return result;
    }

    /**
     * 作废流程，利用删除流程实例实现
     *
     * @param cancelProcessReq 作废流程请求对象: {流程实例ID, 作废原因}
     * @return 作废结果标志, 以success 字段值标识 true/false
     */
    @DELETE
    @ApiOperation(value = "作废流程实例", notes = "作废流程实例，业务审批流程发起后，如果觉得审批流程不想要，可以调用此服务进行审批作废。")
    @ApiImplicitParam(name = "requestObj", dataType = "CancelProcessReq", paramType = "body", required = true)
    @Override
    public ServiceResult cancelProcess(CancelProcessReq cancelProcessReq) {
        ServiceResult result = new ServiceResult<>();
        try {
            processRuntimeModel.cancelProcess(cancelProcessReq);
        } catch (BusinessException be) {
            result.setError(be.getCode(), be.getMessage());
        } catch (Exception e) {
            IfpLogger.error(Thread.currentThread().getStackTrace()[1].getMethodName(), "作废流程业务异常，case: {}",
                Throwables.getStackTraceAsString(e));
            result.setError("ECAP0000", "服务异常，请联系系统管理员");
            return result;
        }
        return result;
    }

    /**
     * 检查用户是否启动某一个流程
     *
     * @param processReq 用户是否重复启动某一流程请求对象
     * @return 是否启动标志, 以 success 字段值标识 true/false
     */
    @POST
    @Path("check-repeat-start")
    @ApiOperation(value = "检查用户是否重复发起某一个流程", notes = "检查某个用户是否已经发起一个相同流程，可控制用户多次发起某一流程，根据用户id和流程定义Key进行查询")
    @ApiImplicitParam(name = "requestObj", dataType = "QueryRepeatStartProcessReq", paramType = "body", required = true)
    @Override
    public ServiceResult checkRepeatStartProcess(QueryRepeatStartProcessReq processReq) {
        ServiceResult result = new ServiceResult<>();
        try {
            processHistoryModel.checkRepeatStartProcess(processReq);
        } catch (BusinessException be) {
            result.setError(be.getCode(), be.getMessage());
        } catch (Exception e) {
            IfpLogger.error(Thread.currentThread().getStackTrace()[1].getMethodName(), "检查用户是否重复发起某一个流程，case: {}",
                Throwables.getStackTraceAsString(e));
            result.setError("ECAP0000", "服务异常，请联系系统管理员");
            return result;
        }
        return result;
    }

    /**
     * 根据流程实例ID查询流程任务处理记录
     *
     * @param processReq 流程信息请求对象: {流程实例id}
     * @return 流程任务走向记录列表
     */
    @POST
    @Path("list-history-tasks")
    @ApiOperation(value = "根据流程实例ID查询流程任务处理记录", notes = "根据流程实例ID查询流程任务处理记录，返回所有已经完成的审批节点数据信息")
    @ApiImplicitParam(name = "requestObj", dataType = "ProcessInstanceReq", paramType = "body", required = true)
    @Override
    public ServiceResult<List<HistoryTaskDTO>> listHistoryTaskByProcessInstanceId(ProcessInstanceReq processReq) {
        ServiceResult<List<HistoryTaskDTO>> result = new ServiceResult<>();
        try {
            result.setResult(processHistoryModel.listHistoryTaskByProcessInstanceId(processReq));
        } catch (BusinessException be) {
            result.setError(be.getCode(), be.getMessage());
        } catch (Exception e) {
            IfpLogger.error(Thread.currentThread().getStackTrace()[1].getMethodName(), "根据流程实例ID查询流程任务处理记录异常，case: {}",
                Throwables.getStackTraceAsString(e));
            result.setError("ECAP0000", "服务异常，请联系系统管理员");
            return result;
        }
        return result;
    }

    /**
     * 根据流程主体人查询是否有在途流程
     *
     * @param req 查询业务客户是否有未结束的流程请求参数
     * @return 是否有在途流程标志, 以 success 字段值标识 true/false
     */
    @POST
    @Path("unfinished-business-user")
    @ApiOperation(value = "根据流程关联业务人查询是否有在途流程",
        notes = "调用发起流程服务时，传入“关联业务人”字段，该服务可根据流程关联业务人查询是否有在途流程<br>关联业务人和流程发起人可以非同一人")
    @ApiImplicitParam(name = "requestObj", dataType = "QueryBusinessUserRunningProcessReq", paramType = "body",
        required = true)
    @Override
    public ServiceResult<Object> listProcessInstanceByBusinessUserId(@Valid QueryBusinessUserRunningProcessReq req) {
        ServiceResult<Object> result = new ServiceResult<>();
        try {
            processRuntimeModel.listProcessInstanceByBusinessUserId(req);
        } catch (BusinessException be) {
            result.setError(be.getCode(), be.getMessage());
        } catch (Exception e) {
            IfpLogger.error(Thread.currentThread().getStackTrace()[1].getMethodName(),
                "根据流程关联业务人查询是否有在途流程服务异常，case: {}", Throwables.getStackTraceAsString(e));
            result.setError("ECAP0000", "服务异常，请联系系统管理员");
            return result;
        }
        return result;
    }

    /**
     * 更改流程优先级
     *
     * @param req 更改流程优先级请求参数
     * @return 以 success 字段值标识 true: 修改成功/false: 修改失败
     */
    @POST
    @Path("update-priority")
    @ApiOperation(value = "更改流程优先级", notes = "该服务可根据实际业务需求，在流程审批途中更改流程优先级")
    @ApiImplicitParam(name = "requestObj", dataType = "PriorityReq", paramType = "body", required = true)
    @Override
    public ServiceResult<Object> updateProcessPriority(PriorityReq req) {
        ServiceResult<Object> result = new ServiceResult<>();
        try {
            processRuntimeModel.updateProcessPriority(req);
        } catch (BusinessException be) {
            result.setError(be.getCode(), be.getMessage());
        } catch (Exception e) {
            IfpLogger.error(Thread.currentThread().getStackTrace()[1].getMethodName(), "更改流程优先级服务异常，case: {}",
                Throwables.getStackTraceAsString(e));
            result.setError("ECAP0000", "服务异常，请联系系统管理员");
            return result;
        }
        return result;
    }

}
